package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type configData struct {
	configMap ConfigType
}

type ConfigType map[string]interface{}

var (
	configCache = configData{
		configMap: make(ConfigType),
	}
)

func InitConfig(fileName string) error {
	var file *os.File
	var err error
	if strings.HasPrefix(fileName, "/") {
		file, err = os.Open(fileName)
	} else {
		file, err = os.Open(fmt.Sprintf("config/%s.json", fileName))
	}
	if err != nil {
		return err
	}
	defer file.Close()

	data, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	var config ConfigType
	err = json.Unmarshal(data, &config)
	if err != nil {
		return err
	}

	configCache = configData{
		configMap: config,
	}

	return nil
}

func GetConfig() ConfigType {
	return configCache.configMap
}

func (cfg ConfigType) Get(name string) (ConfigType, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.(map[string]interface{}); ok {
			return u, nil
		}
		return nil, fmt.Errorf("ConfigType.GetMsgFunc value of key %s is not a ConfigType", name)
	}
	return nil, fmt.Errorf("ConfigType.GetMsgFunc can't find the value of key %s", name)
}

func (cfg ConfigType) GetArray(name string) ([]interface{}, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.([]interface{}); ok {
			return u, nil
		}
		return nil, fmt.Errorf("ConfigType.GetArray value of key %s is not an array", name)
	}
	return nil, fmt.Errorf("ConfigType.GetArray can't find the value of key %s", name)
}

func (cfg ConfigType) GetConfigArray(name string) ([]ConfigType, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.([]interface{}); ok {
			var w = make([]ConfigType, len(u))
			for i := 0; i < len(u); i++ {
				if x, ok := u[i].(map[string]interface{}); ok {
					w[i] = ConfigType(x)
				} else {
					return nil, fmt.Errorf("ConfigType.GetConfigArray one of the items is not a map %d", i)
				}
			}
			return w, nil
		}
		return nil, fmt.Errorf("ConfigType.GetConfigArray value of key %s is not an array", name)
	}
	return nil, fmt.Errorf("ConfigType.GetConfigArray can't find the value of key %s", name)
}

func (cfg ConfigType) GetStringArray(name string) ([]string, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.([]interface{}); ok {
			var w = make([]string, len(u))
			for i := 0; i < len(u); i++ {
				if x, ok := u[i].(string); ok {
					w[i] = x
				} else {
					return nil, fmt.Errorf("ConfigType.GetStringArray one of the items is not a string %d", i)
				}
			}
			return w, nil
		}
		return nil, fmt.Errorf("ConfigType.GetStringArray value of key %s is not an array", name)
	}
	return nil, fmt.Errorf("ConfigType.GetStringArray can't find the value of key %s", name)
}

func (cfg ConfigType) GetInt64Array(name string) ([]int64, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.([]interface{}); ok {
			var w = make([]int64, len(u))
			for i := 0; i < len(u); i++ {
				if x, ok := u[i].(float64); ok {
					w[i] = int64(x)
				} else {
					return nil, fmt.Errorf("ConfigType.GetInt64Array one of the items is not a integer %d", i)
				}
			}
			return w, nil
		}
		return nil, fmt.Errorf("ConfigType.GetInt64Array value of key %s is not an array", name)
	}
	return nil, fmt.Errorf("ConfigType.GetInt64Array can't find the value of key %s", name)
}

func (cfg ConfigType) GetInt64(name string) (int64, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.(float64); ok {
			return int64(u), nil
		}
		return 0, fmt.Errorf("ConfigType.GetInt64 value of key %s is not an int64", name)
	}
	return 0, fmt.Errorf("ConfigType.GetInt64 can't find the value of key %s", name)
}

func (cfg ConfigType) GetBool(name string) (bool, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.(bool); ok {
			return bool(u), nil
		}
		return false, fmt.Errorf("ConfigType.GetBool value of key %s is not an int64", name)
	}
	return false, fmt.Errorf("ConfigType.GetBool can't find the value of key %s", name)
}

func (cfg ConfigType) GetString(name string) (string, error) {
	if v, ok := cfg[name]; ok {
		if u, ok := v.(string); ok {
			return u, nil
		}
		return "", fmt.Errorf("ConfigType.GetString value of key %s is not a string", name)
	}
	return "", fmt.Errorf("ConfigType.GetString can't find the value of key %s", name)
}
